package me.itsoo.artemis.framework.repository.interceptor;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.itsoo.artemis.framework.core.util.ArrayUtils;
import me.itsoo.artemis.framework.core.util.CollectionUtils;
import me.itsoo.artemis.framework.ioc.lang.Matches;
import me.itsoo.artemis.framework.repository.executor.BoundSql;
import me.itsoo.artemis.framework.repository.executor.SqlExecutor;
import me.itsoo.artemis.framework.repository.mapper.MapperDefinition;
import me.itsoo.artemis.framework.repository.mapper.condition.PageCondition;
import me.itsoo.artemis.framework.repository.mapper.condition.QueryCondition;
import me.itsoo.artemis.framework.repository.mapper.resource.QueryResource;
import me.itsoo.artemis.framework.repository.page.PageResult;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 自定义分页插件
 *
 * @author zxy
 * @version 0.0.1
 * @since 2023/5/23
 */
@Slf4j
@Matches(".*Page") // 拦截 Mapper 中为 Page 结尾的方法（正则语法）
public class PaginationPlugin implements SqlInterceptor, Ordered {

    private static final String QUERY_COUNT = "count(*)";

    @Override
    public int ordered() {
        return 100; // 非必需，这里只是说明给拦截器定义排序的两种方式：Ordered、@Orders
    }

    @Override
    @SneakyThrows
    @SuppressWarnings("unchecked")
    public <T> T execute(SqlExecutor sqlExecutor, MapperDefinition md, Invocation invocation) {
        final PageCondition pc = getPageCondition(invocation.getArgs());
        final BoundSql boundSql = getBoundSql(md.getTableName(), pc.getQueryCond());

        final List<Long> totalColl = sqlExecutor.executeSelect(md, boundSql, Long.class);
        final long totalCount = Optional.ofNullable(totalColl.get(0)).orElse(0L);

        return (totalCount == 0)
                ? (T) PageResult.create(pc.getPageNum(), pc.getPageSize(), 0, Collections.emptyList())
                : (T) PageResult.create(pc.getPageNum(), pc.getPageSize(), totalCount, (List<T>) invocation.proceed());
    }

    /**
     * 获取 *Page 方法的 PageCondition 入参
     *
     * @param args Object[]
     * @return PageCondition
     */
    private PageCondition getPageCondition(Object[] args) {
        final List<PageCondition> coll = Arrays.stream(args)
                .filter(PageCondition.class::isInstance)
                .map(PageCondition.class::cast)
                .collect(Collectors.toList());
        return CollectionUtils.findFirstNonNull(coll);
    }

    /**
     * 自定义组装 BoundSql
     *
     * @param tableName String
     * @param queryCond QueryCondition
     * @return BoundSql
     */
    private BoundSql getBoundSql(String tableName, QueryCondition queryCond) {
        final QueryResource res = QueryResource.select(tableName).add(QUERY_COUNT);
        final String sql = res.getResourceCommand();
        final Object[] args = res.getResourceArgs();
        return Objects.nonNull(queryCond)
                ? BoundSql.create(sql + " " + queryCond, ArrayUtils.merge(args, queryCond.getConditionArgs()))
                : BoundSql.create(sql, args);
    }
}
